#include "XmlConnection.h"
#include "mavsprintf.h"
#include "../std.h"
#include "../Utilities/Base64.h"

XmlConnection::XmlConnection(String& username, String& password)
{
  mUsername = username;
  mPassword = password;
  mConn = new HttpConnection(this);
}

XmlConnection::XmlConnection()
{
  mConn = new HttpConnection(this);
}

XmlConnection::~XmlConnection()
{
  if(mConn->isOpen())
    mConn->close();

  delete mConn;
}

//The XmlListener gets data from the Xml document
void XmlConnection::setXmlListener(XmlListener* p)
{
  mProcessor = p;
}

void XmlConnection::setURL(const char* queryURL)
{
  mUrl = queryURL;
}

const char* XmlConnection::getURL()
{
  return mUrl.c_str();
}

void XmlConnection::setHeader(String& header, String& value)
{
  mHeaders[header] = value;
}

void XmlConnection::clearHeaders()
{
  mHeaders.clear();
}

void XmlConnection::setData(String& data)
{
  mCommandData.clear();
  mCommandData.append(data.c_str(), data.length());
}

void XmlConnection::beginDownloading()
{
  hasError = false;
  mIsBusy = true;

  //LOG("Performing Query %s:", mUrl.c_str());
  if(mUrl.length() == 0 || mConn->isOpen())
    return;

  mConn->create(mUrl.c_str(), HTTP_POST);

  //lprintfln("result %d", result);

  if(mUsername.length() > 0)
  {
    char buffer[255];
    sprintf(buffer, "%s:%s", mUsername.c_str(), mPassword.c_str());
    String auth;
    base64_encode(buffer, 1 + mUsername.length() + mPassword.length(), auth);
    char enc[255];
    sprintf(enc, "Basic %s", auth.c_str());
    mConn->setRequestHeader("Authorization", enc);
  }

  //mConn->setRequestHeader("Content-length", "0");

  for(Map<String, String>::Iterator i = mHeaders.begin();
      i != mHeaders.end(); i++)
  {
    mConn->setRequestHeader(i->first.c_str(), i->second.c_str());
  }

  //Send any additional command data
  if(mCommandData.length() > 0)
	  mConn->write(mCommandData.c_str(), mCommandData.length());
}

void XmlConnection::connWriteFinished(Connection* conn, int result){
	APPCONTROLLER->setDataTransfer(mCommandData.length());
	//lprintfln("result %d", result);
	//lprintfln("data : %d", APPCONTROLLER->getDataTransfer());
	mConn->finish();
}

void XmlConnection::xcConnError(int code)
{
  //LOG("Conn error: %d - %s", code, mUrl.c_str());
  stop();
}

void XmlConnection::httpFinished(HttpConnection* conn, int result)
{
  //LOG("HttpFinished - starting parser");
  //LOG("HttpResult %d %s", result, mUrl.c_str());

  switch(result)
  {
	  case 200:
		  //OK
		  mContext.init(this, mProcessor);
		  mPtr = mBuffer;
		  break;
	  case 301:
		  //Content redirected.  The new URL will be in the payload
		  mIsRedirecting = true;
		  break;
	  default:
		  //Error
		  hasError = true;
		  xcConnError(result);
		  return;
  }

  String contentLengthStr;
  int responseBytes = mConn->getResponseHeader("content-length", &contentLengthStr);
  int contentLength = 0;
  if(responseBytes != CONNERR_NOHEADER){
	  contentLength = atoi(contentLengthStr.c_str());
	  APPCONTROLLER->setDataTransfer(contentLength);
  }

  //lprintfln("data : %d", APPCONTROLLER->getDataTransfer());

  //LOG("Requesting payload");
  mConn->recv(mBuffer, sizeof(mBuffer)-1);
}


void XmlConnection::connRecvFinished(Connection* conn, int result)
{
    //LOG("Received some data");
	if(result < 0)
	{
        xcConnError(result);
        return;
    }

    if(hasError)
    {
      //LOG("%s", mBuffer);
      mConn->close();
    }

    if(mIsRedirecting)
    {
      mConn->close();
      mIsRedirecting = false;
      mUrl.clear();
      mUrl = mBuffer;
      //LOG("Redirecting URL to %s", mUrl.c_str());
      //Call again with the new URL
      beginDownloading();
    }

    mPtr[result] = 0;
    mPtr = mBuffer;

    //lprintfln(mBuffer);

    //LOG("Passing to XmlParser");
    //By calling feedProcess, the parser will also convert UTF-8 characters and
    //entity references into Latin-1 characters.  You need to do this if you want to
    //use the data in MAUI.
    bool stopped = mContext.feedProcess(mBuffer);
    if(!stopped) {  //parsing may have been interrupted by stop().
      mConn->recv(mPtr, sizeof(mBuffer) - 1 - (mPtr - mBuffer));
    }
}

void XmlConnection::mtxDataRemains(const char* data, int len)
{
    if(mBuffer != data) {
        memcpy(mBuffer, data, len);
    }
    mPtr = mBuffer + len;
}

void XmlConnection::stop()
{
    if(mConn != NULL)
      mConn->close();
    mContext.stop();
    mIsBusy = false;
}

int XmlConnection::process(char* data)
{
    return mContext.process(data);
}

bool XmlConnection::isBusy()
{
  return mIsBusy;
}

void XmlConnection::close()
{
	stop();
}
